//
// Created by d on 11/10/18.
//

#ifndef RAYTRACING_CPUTRACER_H
#define RAYTRACING_CPUTRACER_H

#include <Eigen/Dense>
#include <vector>

using namespace Eigen;
using namespace std;

union GlElement {
    float f;
    int i;
};

struct ShaderElement {
    GlElement positionX;
    GlElement positionY;
    GlElement positionZ;
    GlElement textureX;
    GlElement textureY;
};

template <typename T>
class VectorCreator {
private:
    vector<T> m_vec;
public:
    explicit VectorCreator(const T& val) {
        m_vec.push_back(val);
    }

    VectorCreator<T>& operator()(const T& val) {
        m_vec.push_back(val);
        return *this;
    }

    explicit operator vector<T>() {
        return m_vec;
    }
};


class Point {
public:
    Vector3f p;
    Point();
    Point(float x, float y, float z);
    explicit Point(const Vector3f & pp);
    Point(const Point & pp);
};


class Ray {
public:
    Point o;
    Vector3f direction;
    Ray(Point origin, Point p);
    Ray(Point origin, Vector3f d);
    Vector3f operator () (float t);
};


class Object {
public:
    virtual int in(const Point & p) = 0;
    virtual float firstIntersection(const Ray &) = 0;
//    virtual Vector3f firstIntersection(const Vector3f & p1, const Vector3f *p2);
    virtual
        pair<vector<ShaderElement>, vector<int> >
        triangulate();
    virtual pair<vector<Vector3f>, vector<int> >
        _triangulate()=0;
    virtual ~Object() = default;
};


class Sphere: public Object {
public:
    Sphere(Point c, float r);
    Sphere();
    Point center;
    float r;
    int in(const Point &p) override;
    float firstIntersection(const Ray &ray) override;
    pair<vector<Vector3f>, vector<int> >
        _triangulate() override;
};


class IntersectObject: public Object{
public:
    int in(const Point &p) override;
    pair<vector<Vector3f>, vector<int> >
    _triangulate() override;
    float firstIntersection(const Ray &ray) override;
    void addObject(Object * object);
private:
    vector<Object *> objects;
};


template<class T>
bool anyOf(vector<T> v, std::function<bool(T)> anyfuck ) {
    for(auto field:v){
        if(anyfuck(field)){
            return true;
        }
    }
    return false;
}

template<class T>
bool allOf(vector<T> v, std::function<bool(T)> anyfuck ) {
    for(auto field:v){
        if(!anyfuck(field)){
            return false;
        }
    }
    return true;
}


#endif //RAYTRACING_CPUTRACER_H
